package cn.gztang.util;

import cn.gztang.exception.QRCodeCreateFailException;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;

/**
 * @author GaoFeng2017
 * @date 2018/9/5 18:59
 */
public class QRCodeUtil {

    private static final int FONT_SIZE = 15;
    private static final int CODE_HEIGHT = 30;

    /**
     *
     * 创建二维码，并输出到流中
     *
     * @author ZengZhengFang
     *
     * @date 2018/9/5 19:27
     *
     * @param out
     * @param message
     * @param width
     * @param height
     * @param format
     *
     * @return void
     *
     */
    public static void createQRCodeToStream(OutputStream out, String message, int width, int height, String format) throws QRCodeCreateFailException {

        BitMatrix bitMatrix = createQRCodeBitMatrix(message, width, height);

        writeToStream(bitMatrix,format,out);

    }

    /**
     *
     * 创建二维码图片，并输出到指定路径
     *
     * @author ZengZhengFang
     *
     * @date 2018/9/5 19:27
     *
     * @param filePath
     * @param message
     * @param width
     * @param height
     * @param format
     *
     * @return void
     *
     */
    public static void createQRCodeToFile(String filePath, String message, int width, int height, String format) throws QRCodeCreateFailException {

        BitMatrix bitMatrix = createQRCodeBitMatrix(message, width, height);

        writeToFile(bitMatrix,format,filePath);

    }

    /**
     * 创建条形码，并输出到流中
     * @author ZengZhengFang
     *
     * @date 2018/9/5 20:30
     *
     * @param out
     * @param message
     * @param width
     * @param height
     * @param format
     *
     * @return void
     *
     */
    public static void createBarcodeToStream(OutputStream out, String message, int width, int height, String format) throws QRCodeCreateFailException {

        BitMatrix bitMatrix = createBarcodeBitMatrix(message, width, height);

        writeToStream(bitMatrix,format,out);

    }

    /**
     *
     * 创建条形码图片，并输出到指定路径
     *
     * @author ZengZhengFang
     *
     * @date 2018/9/5 20:32
     *
     * @param filePath
     * @param message
     * @param width
     * @param height
     * @param format
     *
     * @return void
     *
     */
    public static void createBarcodeToFile(String filePath, String message, int width, int height, String format) throws QRCodeCreateFailException {

        BitMatrix bitMatrix = createBarcodeBitMatrix(message, width, height);

        writeToFile(bitMatrix,format,filePath);

        createFont(message,filePath,"jpg",width,height);
    }

    public static void createFont(String no , String filePath, String imageType, int width, int height) throws QRCodeCreateFailException {

        height += 22;

        BufferedImage font = new BufferedImage(width, height,
            BufferedImage.TYPE_INT_RGB);
        BufferedImage code = null;
        try {
            code = ImageIO.read(new File(filePath));
        } catch (IOException e) {
            throw new QRCodeCreateFailException(e);

        }
        Graphics2D g = (Graphics2D) font.getGraphics();
        //长宽是总的 字体加二维码的
        g.clearRect(0, 0, width, height);
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, width, height);
        //字体渲染
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g.setRenderingHint(RenderingHints.KEY_RENDERING,
            RenderingHints.VALUE_RENDER_QUALITY);
        //在图片上把字写好
        for (int i = 0; i < no.length(); i++) {
            g.setColor(Color.black);
            Font font_ = new Font("Consolas", 0, FONT_SIZE);
            g.setFont(font_);
            // (FONT_SIZE * 2 + width - no.length()
            //     * FONT_SIZE)
            //     / 2
            g.drawString(no.charAt(i) + "",  FONT_SIZE + 10 + (i - 1) * FONT_SIZE - 5 * i, height - 5);
        }
        //然后把二维码加上去
        g.drawImage(code, 0, 0, null);
        g.dispose();

        //进行图片处理,防止出现模糊
        int[] rgb = new int[3];
        for (int i = 0; i < width; i++) {
            for (int j = CODE_HEIGHT; j < height; j++) {
                int pixel = font.getRGB(i, j);
                rgb[0] = (pixel & 0xff0000) >> 16;
                rgb[1] = (pixel & 0xff00) >> 8;
                rgb[2] = (pixel & 0xff);
                if (rgb[0] > 125 || rgb[1] > 125 || rgb[2] > 125) {
                    font.setRGB(i, j, -1);
                }
                if (rgb[0] < 100 || rgb[1] < 100 || rgb[2] < 100) {
                    font.setRGB(i, j, -16777216);
                }
            }
        }

        File outputFile = new File(filePath);
        try {
            ImageIO.write(font, imageType, outputFile);
        } catch (IOException e) {
            throw new QRCodeCreateFailException(e);
        }
    }

    private static BitMatrix createBarcodeBitMatrix(String message, int width, int height) throws QRCodeCreateFailException {

        if (StringUtil.isBlank(message)) {
            throw new QRCodeCreateFailException("条形码内容不能为空");
        }

        HashMap<EncodeHintType, Object> hints = new HashMap<>(2);
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");

        BitMatrix bitMatrix = null;

        try {
            bitMatrix = new MultiFormatWriter().encode(message, BarcodeFormat.CODE_128, width, height, hints);
        } catch (WriterException e) {
            throw new QRCodeCreateFailException(e);
        }

        return bitMatrix;
    }

    private static void writeToStream(BitMatrix bitMatrix, String format, OutputStream out) throws QRCodeCreateFailException {
        try {
            MatrixToImageWriter.writeToStream(bitMatrix, format, out);
        } catch (IOException e) {
            throw new QRCodeCreateFailException(e);
        }
    }

    private static void writeToFile(BitMatrix bitMatrix, String format, String filePath) throws QRCodeCreateFailException {
        try {
            MatrixToImageWriter.writeToPath(bitMatrix, format, new File(filePath).toPath());
        } catch (IOException e) {
            throw new QRCodeCreateFailException(e);
        }
    }


    private static BitMatrix createQRCodeBitMatrix(String message, int width, int height) throws QRCodeCreateFailException {

        int cap = (int) (3 / 0.75 + 1);
        HashMap<EncodeHintType, Object> hints = new HashMap<>(cap);
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        hints.put(EncodeHintType.MARGIN, 2);

        BitMatrix bitMatrix = null;

        try {
            bitMatrix = new MultiFormatWriter().encode(message, BarcodeFormat.QR_CODE, width, height, hints);
        } catch (WriterException e) {
            throw new QRCodeCreateFailException(e);
        }

        return bitMatrix;
    }
}
